package com.justdoit.facedemo.factory;

import com.arcsoft.face.EngineConfiguration;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FunctionConfiguration;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.justdoit.facedemo.config.EngineConfigurationProperty;
import com.justdoit.facedemo.config.FunConfigurationProperty;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "arcface")
@EnableConfigurationProperties({
        FunConfigurationProperty.class,
        EngineConfigurationProperty.class})

public class ArcFaceConfig {
    private String appId;
    private String sdkKey;

    // 64 脚本位置，当我们进行完第一次注册引擎之后，后面就不需要在进行注册了，因为这个脚本里面保存了相关信息
    private String dllPath;

    @Bean
    public FaceEngine faceEngine(EngineConfigurationProperty engineConfigurationProperty, FunConfigurationProperty funConfigurationProperty) {
        // 创建引擎，并传入脚本位置
        FaceEngine faceEngine = new FaceEngine(dllPath);

        // 一个状态码的标识位
        int errorCode;


        // 使用 activeOnline()方法，联网注册引擎 ，仅第一次需要调用，成功返回 ErrorInfo.MOK、ErrorInfo.MERR_ASF_ALREADY_ACTIVATED
        errorCode = faceEngine.activeOnline(appId, sdkKey);
        if (errorCode != ErrorInfo.MOK.getValue() &&
                errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue())
            throw new RuntimeException("引擎注册失败");




        //初始化引擎，根据配置类，来进行配置引擎
        EngineConfiguration engineConfiguration = getFaceEngineConfiguration(engineConfigurationProperty, funConfigurationProperty);


        // init() ：用来初始化引擎，成功返回ErrorInfo.MOK
        // 这里面的引擎，是根据我们yaml配置类，进行创建的
        errorCode = faceEngine.init(engineConfiguration);
        if (errorCode != ErrorInfo.MOK.getValue())
            throw new RuntimeException("初始化引擎失败");

        // 一切都配置完毕，返回faceEngine到容器中，后续的人脸识别，对比，都是通过这个进行
        return faceEngine;
    }

    /**
     * 初始化引擎配置，就是根据我们前面配置的两个
     * 用来读取yaml文件的类，来进行一些参数配置
     *
     * @return 返回引擎对象配置类
     */
    private EngineConfiguration getFaceEngineConfiguration(EngineConfigurationProperty engineConfigurationProperty, FunConfigurationProperty funConfigurationProperty) {

        // 创建引擎对象
        EngineConfiguration engineConfiguration = new EngineConfiguration();

      /*  1.这里是根据yaml 配置类，配置引擎模式
          2.有两种模式：ASF_DETECT_MODE_VIDEO模式、ASF_DETECT_MODE_IMAGE模式

           *ASF_DETECT_MODE_VIDEO 模式：
               对视频流中的人脸进行追踪，人脸框平滑过渡，不会出现跳框的现象。
               用于预览帧数据的人脸追踪，使用该模式比使用IMAGE模式处理速度更快，可避免出现卡顿问题。

               在视频模式下人脸追踪会带有一个faceId值，该值用于标记一张人脸，当一个人脸从进入画面
               直到离开画面，faceId值不变，这可用于业务中优化程序性能。

            *ASF_DETECT_MODE_IMAGE 模式：
                处理静态图像数据，类似注册人脸库时，推荐使用IMAGE模式
                若同时需要进行IMAGE模式人脸检测和VIDEO模式人脸检测，
                需要创建一个VIDEO模式的引擎和一个IMAGE模式的引擎；
      */
        if ("VVIDEO".equals(engineConfigurationProperty.getDetectMode()))
            engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_VIDEO);
        else {
            engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
        }

        /*
            1.根据yaml配置文件，配置人脸角度：
                * ASF_OP_ALL_OUT  全角度，不够准确且检测速度慢
                * ASF_OP_0_ONLY   逆时针0度，
                * ASF_OP_90_ONLY  逆时针90度，
                * ASF_OP_180_ONLY 逆时针180度
                * ASF_OP_270_ONLY 逆时针270度

             2.设置0度方向并不是说只有0度角可以检测到人脸，而是大体在这个方向上的人脸均可以
               推荐使用单一角度进行检测，因为单一角相对于全角度性能更好、精度更高
         */
        switch (engineConfigurationProperty.getDetectFaceOrientPriority()) {
            case "ASF_OP_0_ONLY":
                engineConfiguration
                        .setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
                break;
            case "ASF_OP_90_ONLY":
                engineConfiguration
                        .setDetectFaceOrientPriority(DetectOrient.ASF_OP_90_ONLY);
                break;
            case "ASF_OP_270_ONLY":
                engineConfiguration
                        .setDetectFaceOrientPriority(DetectOrient.ASF_OP_270_ONLY);
                break;
            case "ASF_OP_180_ONLY":
                engineConfiguration
                        .setDetectFaceOrientPriority(DetectOrient.ASF_OP_180_ONLY);
                break;
            case "ASF_OP_ALL_OUT":
                engineConfiguration
                        .setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
                break;
            default:
                engineConfiguration
                        .setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
        }


        /*
            1.设置最小的，人脸识别比列，就是一张人脸，在一张照片中，所占的最小比列
            2.识别的最小人脸比例 = 图片长边 / 人脸框长边的比值
              示例：
                     图片尺寸 400 x 600
                     人脸尺寸 300 x 300
                     minScale = 600 / 300

            3.若为小数，向上取整（例如：2.53 取值为 3）
            4.设置推荐：
              门禁场景下我们推荐使用VIDEO模式，detectFaceScaleVal设置为16，
              但是如果用户使用场景下的成像质量比较高，这时我们设置为32，在相对较远的位置就可以检测到人脸并比对通过，这样效果更佳，达到无感通行
         */
        engineConfiguration.setDetectFaceScaleVal(engineConfigurationProperty.getDetectFaceScale());


        // 设置可以识别到的最大人脸个数
        engineConfiguration.setDetectFaceMaxNum(engineConfigurationProperty.getDetectFaceMaxNum());


        // 自定义功能配置方法，详情参考下面方法
        initFuncConfiguration(engineConfiguration, funConfigurationProperty);
        return engineConfiguration;
    }

    /**
     * 功能配置，根据yaml配置文件，来配置我们的算法的常量值，以实现不同功能的效果
     *
     * @param engineConfiguration      引擎类
     * @param funConfigurationProperty yaml 配置实体类
     */

    private void initFuncConfiguration(EngineConfiguration engineConfiguration,
                                       FunConfigurationProperty funConfigurationProperty) {

        // 创建配置对象
        FunctionConfiguration functionConfiguration = new FunctionConfiguration();

        //是否支持年龄检测
        functionConfiguration.setSupportAge(funConfigurationProperty.isSupportAge());

        //是否支持3d 检测
        functionConfiguration
                .setSupportFace3dAngle(funConfigurationProperty.isSupportFace3dAngle());

        //是否支持人脸检测
        functionConfiguration
                .setSupportFaceDetect(funConfigurationProperty.isSupportFaceDetect());

        //是否支持人脸识别
        functionConfiguration
                .setSupportFaceRecognition(funConfigurationProperty.isSupportFaceRecognition());

        //是否支持性别检测
        functionConfiguration
                .setSupportGender(funConfigurationProperty.isSupportGender());

        //是否支持活体检测
        functionConfiguration
                .setSupportLiveness(funConfigurationProperty.isSupportLiveness());

        //是否至此IR活体检测
        functionConfiguration
                .setSupportIRLiveness(funConfigurationProperty.isSupportIRLiveness());

        // 使用该配置对象，成功返回ErrorInfo.MOK
        engineConfiguration.setFunctionConfiguration(functionConfiguration);
    }
}
